﻿/*

The MIT License

Copyright (c) 2010 Cartesian Analytics, Inc. 

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

*/

using System;
using System.Collections.Generic;
using System.Linq;

namespace Pipra.Uom
{
    /// <summary>
    /// A value converter based on a fraction.
    /// </summary>
    public class FactorValueConverter :
        ValueConverterBase
    {

        private class FactorValueConverterMul : FactorValueConverter
        {

            private readonly double _mulValue;

            public FactorValueConverterMul(string type, IUom from, IUom to, ConversionFactorEntry entry)
                : base(type, from, to, entry)
            {
                _mulValue = entry.Numerator;
            }

            public override void TransformValue(ref double value)
            {
                value *= _mulValue;
            }

            public override double TransformValue(double value)
            {
                return _mulValue * value;
            }

        }

        private class FactorValueConverterDiv : FactorValueConverter
        {

            private readonly double _divValue;

            public FactorValueConverterDiv(string type, IUom from, IUom to, ConversionFactorEntry entry)
                : base(type, from, to, entry)
            {
                _divValue = entry.Denominator;
            }

            public override void TransformValue(ref double value)
            {
                value /= _divValue;
            }

            public override double TransformValue(double value)
            {
                return value / _divValue;
            }

        }

        /// <summary>
        /// Creates a converter.
        /// </summary>
        /// <param name="type">The unit type.</param>
        /// <param name="from">The unit the converter converts from.</param>
        /// <param name="to">The unit the converter converts to.</param>
        /// <param name="entry">The fraction.</param>
        /// <returns></returns>
        public static FactorValueConverter CreateConverter(string type, IUom from, IUom to, ConversionFactorEntry entry)
        {
            if (null == entry)
            {
                throw new ArgumentNullException("entry");
            }
            return (
                1 == entry.Denominator
                    ? new FactorValueConverterMul(type, from, to, entry)
                    : (
                        1 == entry.Numerator
                        ? new FactorValueConverterDiv(type, from, to, entry)
                        : new FactorValueConverter(type, from, to, entry)
                    )
            );
        }

        private readonly ConversionFactorEntry _entry;

        private FactorValueConverter(string type, IUom from, IUom to, ConversionFactorEntry entry)
            : base(type, from, to)
        {
            _entry = entry;
        }

        /// <summary>
        /// The fraction used.
        /// </summary>
        public ConversionFactorEntry Entry
        {
            get { return _entry; }
        }

        /// <summary>
        /// The estimated complexity of the conversion.
        /// </summary>
        public override int OpCost
        {
            get { return _entry.OpCount; }
        }

        public override double TransformValue(double value)
        {
            return (value * _entry.Numerator) / _entry.Denominator;
        }

        public override void TransformValue(ref double value)
        {
            value = (value * _entry.Numerator) / _entry.Denominator;
        }

        public override void TransformValues(double[] values)
        {
            for (int i = 0; i < values.Length; i++)
            {
                TransformValue(ref values[i]);
            }
        }

        public override IEnumerable<double> TransformValues(IEnumerable<double> values)
        {
            return values.Select(TransformValue);
        }

        public override bool HasInverse
        {
            get { return 0 != _entry.Numerator; }
        }

        public override IValueConverter<double> GetInverse()
        {
            return new FactorValueConverter(Type, To, From, ConversionFactorEntry.GetInverted(_entry));
        }
    }
}
